/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-05-05     vandoul      first
 */

#include <rtthread.h>
#include "cy_gpio.h"
#include "cyhal_gpio.h"
#include "cyhal_sdhc.h"


#define DRV_DEBUG
#define LOG_TAG             "drv.sdio"
#include <drv_log.h>

#define SDIO_BLOCK_SIZE                         (512)


struct _cy_sdio_pin_and_name_config
{
    const char *name;
    cyhal_gpio_t cmd;               /**< The pin connected to the command signal. */
    cyhal_gpio_t clk;               /**< The pin connected to the clock signal. */
    cyhal_gpio_t data0;             /**< The pin connected to the data0 signal. */
    cyhal_gpio_t data1;             /**< The pin connected to the data1 signal. */
    cyhal_gpio_t data2;             /**< The pin connected to the data2 signal. */
    cyhal_gpio_t data3;             /**< The pin connected to the data3 signal. */
    cyhal_gpio_t data4;             /**< The pin connected to the data4 signal; pass NC when unused. */
    cyhal_gpio_t data5;             /**< The pin connected to the data5 signal; pass NC when unused. */
    cyhal_gpio_t data6;             /**< The pin connected to the data6 signal; pass NC when unused. */
    cyhal_gpio_t data7;             /**< The pin connected to the data7 signal; pass NC when unused. */
    cyhal_gpio_t card_detect;       /**< The pin connected to the card detect signal. */
    cyhal_gpio_t io_volt_sel;       /**< The pin connected to the voltage select signal. */
    cyhal_gpio_t card_if_pwr_en;    /**< The pin connected to the card interface power enable signal. */
    cyhal_gpio_t card_mech_write_prot; /**< The pin connected to the write protect signal. */
    cyhal_gpio_t led_ctrl;          /**< The pin connected to the LED control signal. */
    cyhal_gpio_t card_emmc_reset;   /**< The pin connected to the eMMC card reset signal. */
};

static const struct _cy_sdio_pin_and_name_config _sdcard_config =
{
    .name = "sd0",
    .cmd                  = CYHAL_GET_GPIO(CYHAL_PORT_2, 4),
    .clk                  = CYHAL_GET_GPIO(CYHAL_PORT_2, 5),
    .data0                = CYHAL_GET_GPIO(CYHAL_PORT_2, 0),
    .data1                = CYHAL_GET_GPIO(CYHAL_PORT_2, 1),
    .data2                = CYHAL_GET_GPIO(CYHAL_PORT_2, 2),
    .data3                = CYHAL_GET_GPIO(CYHAL_PORT_2, 3),
    .data4                = NC,
    .data5                = NC,
    .data6                = NC,
    .data7                = NC,
    .card_detect          = CYHAL_GET_GPIO(CYHAL_PORT_2, 6),
    .io_volt_sel          = NC,
    .card_if_pwr_en       = NC,
    .card_mech_write_prot = NC,
    .led_ctrl             = NC,
    .card_emmc_reset      = NC,
};

#include <dfs_fs.h>
#include <drivers/mmcsd_core.h>
#include <drivers/gpt.h>
struct rthw_sdio
{
    struct rt_device parent;
    cyhal_sdhc_t sdhc_obj; /**< Object for use with the SDHC HAL driver. */
    cyhal_sdhc_config_t sdhc_config; /**< Card configuration structure to be passed to the HAL driver. */
};


rt_uint8_t *sector = NULL;

static rt_err_t rt_mmcsd_init(rt_device_t dev)
{
    return RT_EOK;
}

static rt_err_t rt_mmcsd_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

static rt_err_t rt_mmcsd_close(rt_device_t dev)
{
    return RT_EOK;
}

static rt_size_t rt_mmcsd_read(rt_device_t dev,rt_off_t pos,void *buffer, rt_size_t size)
{
    rt_err_t err = 0;
    struct rthw_sdio *sdio = (struct rthw_sdio *)dev;
    cyhal_sdhc_t *hw_sdio = &sdio->sdhc_obj;
    uint32_t addr = pos;
    LOG_D("mmc read:pos:%d size:%d", pos, size);
    if (dev == RT_NULL) return 0;

    do {
        size_t block_count =  size;

        cy_rslt_t result = cyhal_sdhc_read(hw_sdio, addr, buffer, &block_count);
        if(CY_RSLT_SUCCESS != result){
            rt_kprintf("read fail\n");
            err = -RT_ERROR;
            break;
        }else{
            rt_kprintf("read success\n");
        }

    }while(0);

    /* the length of reading must align to SECTOR SIZE */
    if (err)
    {
        rt_set_errno(-RT_EIO);
        return 0;
    }
    return size;
}

void showData()
{
    int inx = 0;
    rt_kprintf("data is :");
    while(inx <= 510){
        rt_kprintf("0x%X ",sector[inx]);
        inx++;
    }
    rt_kprintf("0x%X\n",sector[511]);
}

static rt_size_t rt_mmcsd_write(rt_device_t dev, rt_off_t pos,const void *buffer, rt_size_t   size)
{
    rt_err_t err = 0;
    struct rthw_sdio *sdio = (struct rthw_sdio *)dev;
    cyhal_sdhc_t *hw_sdio = &sdio->sdhc_obj;
    uint32_t addr = pos;
    size_t block_count =  size ;
    LOG_D("mmc write: pos:%d size:%d",  pos, size);
    if (dev == RT_NULL)
    {
        rt_set_errno(-RT_EINVAL);
        return 0;
    }

    do {
        cy_rslt_t result = cyhal_sdhc_write(hw_sdio, addr, buffer, &block_count);
        if(CY_RSLT_SUCCESS != result)
        {
            err = -RT_ERROR;
            break;
        }
    }while(0);

    /* the length of reading must align to SECTOR SIZE */
    if (err)
    {
        rt_set_errno(-RT_EIO);
        return 0;
    }
    return size;
}

int rt_hw_sdio_init(void)
{
    struct rthw_sdio *sdio = RT_NULL;
    sector = rt_malloc(512);

    sdio = rt_malloc(sizeof(struct rthw_sdio));
    if (sdio == RT_NULL)
    {
        LOG_E("malloc rthw_sdio fail");
        return RT_NULL;
    }
    rt_memset(sdio, 0, sizeof(struct rthw_sdio));

    /* register mmcsd device */
    sdio->parent.type  = RT_Device_Class_Block;
    sdio->parent.init = rt_mmcsd_init;
    sdio->parent.open = rt_mmcsd_open;
    sdio->parent.close = rt_mmcsd_close;
    sdio->parent.read = rt_mmcsd_read;
    sdio->parent.write = rt_mmcsd_write;

    do {
        sdio->sdhc_config.enableLedControl = false;
        sdio->sdhc_config.isEmmc = false;
        sdio->sdhc_config.lowVoltageSignaling = false;
        sdio->sdhc_config.busWidth = 4;
        /* Initialize the SD Card interface. */
        int rslt = cyhal_sdhc_init_hw(&sdio->sdhc_obj, &sdio->sdhc_config, _sdcard_config.cmd,   _sdcard_config.clk,
                                 _sdcard_config.data0,  _sdcard_config.data1, _sdcard_config.data2, _sdcard_config.data3,
                                 _sdcard_config.data4,  _sdcard_config.data5, _sdcard_config.data6, _sdcard_config.data7,
                                 _sdcard_config.card_detect, _sdcard_config.io_volt_sel, _sdcard_config.card_if_pwr_en,
                                 _sdcard_config.card_mech_write_prot, _sdcard_config.led_ctrl, _sdcard_config.card_emmc_reset, RT_NULL);
        if(rslt != CY_RSLT_SUCCESS){
            LOG_E("sdhc hw init fail: (0x%x)", rslt);
            break;
        }

        rslt = cyhal_sdhc_init_card(&sdio->sdhc_obj);
        if(rslt != CY_RSLT_SUCCESS)
        {
            LOG_E("sdhc init fail: (0x%x)", rslt);
            break;
        }

        rt_uint32_t block_count;
        rslt = cyhal_sdhc_get_block_count(&sdio->sdhc_obj, &block_count);
        if(rslt != CY_RSLT_SUCCESS){
            LOG_E("get block count fail: (0x%x)", rslt);
            break;
        }
        LOG_D("block count:%d(0x%x)", block_count, block_count);

        rt_memset(sector, 3, 512);

        rt_mmcsd_write(&sdio->parent,0,sector,1);
        rslt = rt_mmcsd_read(&sdio->parent, 0, sector, 1);
        if(rslt < 0){
            LOG_E("rt_mmcsd_read fail");
            break;
        }else{
            LOG_E("rt_mmcsd_read success [%d]\n",rslt);
        }
        showData();

        rslt = rt_device_register(&(sdio->parent), _sdcard_config.name,
                       RT_DEVICE_FLAG_RDWR);

        if(rslt != RT_EOK) {
            LOG_E("register device fail!");
            break;
        }

        return RT_EOK;
    }while(0);

    if(sector) rt_free(sector);
    if(sdio){
        cyhal_sdhc_free(&sdio->sdhc_obj);
        rt_free(sdio);
    }

    return -RT_ERROR;
}
INIT_DEVICE_EXPORT(rt_hw_sdio_init);

